// 构建一个Axios构造函数，核心代码为request

class Axios {
  constructor() {

  }

  request(config) {
      return new Promise(resolve => {
          const {url = '', method = 'get', data = {}} = config;
          // 发送ajax请求
          const xhr = new XMLHttpRequest();
          xhr.open(method, url, true);
          xhr.onload = function() { // 请求已完成，完成后的操作
              console.log(xhr.responseText)
              resolve(xhr.responseText);
          }
          xhr.send(data);
      })
  }
}

// 最终导出axios的方法，即实例的request方法
function CreateAxiosFn() {
  let axios = new Axios();
  let req = axios.request.bind(axios);
  return req;
}

// 得到最后的全局变量axios
let axios = CreateAxiosFn();

// 上述就已经能够实现axios({ })这种方式的请求
// 下面是来实现下axios.method()这种形式的请求
// 定义get,post...方法，挂在到Axios原型上
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
    Axios.prototype[met] = function() {
        console.log('执行'+met+'方法');
        // 处理单个方法
        if (['get', 'delete', 'head', 'options'].includes(met)) { // 2个参数(url[, config])
            return this.request({
                method: met,
                url: arguments[0],
                ...arguments[1] || {}
            })
        } else { // 3个参数(url[,data[,config]])
            return this.request({
                method: met,
                url: arguments[0],
                data: arguments[1] || {},
                ...arguments[2] || {}
            })
        }

    }
})

// 将Axios.prototype上的方法搬运到request上
// 首先实现个工具类，实现将b方法混入到a，并且修改this指向
const utils = {
  extend(a,b, context) {
    for(let key in b) {
      if (b.hasOwnProperty(key)) {
        if (typeof b[key] === 'function') {
          a[key] = b[key].bind(context);
        } else {
          a[key] = b[key]
        }
      }
      
    }
  }
}

// 修改导出的方法
function CreateAxiosFn() {
  let axios = new Axios();
  
  let req = axios.request.bind(axios);
  // 增加代码
  utils.extend(req, Axios.prototype, axios)
  
  return req;
}

// 构建拦截器的构造函数
class InterceptorsManage {
  constructor() {
    this.handlers = [];
  }

  use(fullfield, rejected) {
    this.handlers.push({
      fullfield,
      rejected
    })
  }
}
// 实现axios.interceptors.response.use和axios.interceptors.request.use
class Axios {
  constructor() {
      // 新增代码
      this.interceptors = {
          request: new InterceptorsManage,
          response: new InterceptorsManage
      }
  }

  request(config) {
//  ...
  }
}

// 执行语句axios.interceptors.response.use和axios.interceptors.request.use的时候，实现获取axios实例上的interceptors对象，然后再获取response或request拦截器，再执行对应的拦截器的use方法
// 把Axios上的方法和属性搬到request过去
function CreateAxiosFn() {
  let axios = new Axios();
  
  let req = axios.request.bind(axios);
  // 混入方法， 处理axios的request方法，使之拥有get,post...方法
  utils.extend(req, Axios.prototype, axios)
  // 新增代码
  utils.extend(req, axios)
  return req;
}

// 现在request也有了interceptors对象，在发送请求的时候，会先获取request拦截器的handlers的方法来执行
// 首先将执行ajax的请求封装成一个方法
request(config);{
  this.sendAjax(config)
}
sendAjax(config);{
  return new Promise(resolve => {
      const {url = '', method = 'get', data = {}} = config;
      // 发送ajax请求
      console.log(config);
      const xhr = new XMLHttpRequest();
      xhr.open(method, url, true);
      xhr.onload = function() {
          console.log(xhr.responseText)
          resolve(xhr.responseText);
      };
      xhr.send(data);
  })
}

// 获得handlers中的回调
request(config) ;{
  // 拦截器和请求组装队列
  let chain = [this.sendAjax.bind(this), undefined] // 成对出现的，失败回调暂时不处理

  // 请求拦截
  this.interceptors.request.handlers.forEach(interceptor => {
      chain.unshift(interceptor.fullfield, interceptor.rejected)
  })

  // 响应拦截
  this.interceptors.response.handlers.forEach(interceptor => {
      chain.push(interceptor.fullfield, interceptor.rejected)
  })

  // 执行队列，每次执行一对，并给promise赋最新的值
  let promise = Promise.resolve(config);
  while(chain.length > 0) {
      promise = promise.then(chain.shift(), chain.shift())
  }
  return promise;
}

// chains大概是['fulfilled1','reject1','fulfilled2','reject2','this.sendAjax','undefined','fulfilled2','reject2','fulfilled1','reject1']这种形式

// 这样就能够成功实现一个简易版axios